Android的Context功能

1.获取Resiurces,从而得到drawable,color,string 等
2.启动指定的组件
3.获取系统服务
4.获取目录

错误使用Context导致内存泄漏

错误使用Context可能会导致内存泄漏,经典的例子就是在实现单例模式是使用Context,如下代码是可能会导致内存泄漏的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingleInstance {
private Context mContext;

private static SingleInstance sInstance;
public SingleInstance(Context context) {
mContext = context;
}

public static SingleInstance getsInstance(Context context){
if (sInstance == null){
sInstance = new SingleInstance(context);
}
return sInstance;
}
}

如果使用者调用getInstance时传入的Context是一个Activity或者Service的实例,那么在应用退出之前,由于单例一直存在,会导致对应的Activity或者Service被单例引用,从而不会被垃圾回收,Activity或者Service中关联的其他View或者数据结构对象也不会被释放,从而导致内存泄漏,正确的做法使用Appliction Context,因为他是应用唯一的,而且生命周期是跟应用一致的,正确的使用单例的实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class SingleInstance {
private Context mContext;

private static SingleInstance sInstance;
public SingleInstance(Context context) {
mContext = context;
}

public static SingleInstance getsInstance(Context context){
if (sInstance == null){
sInstance = new SingleInstance(context.getApplicationConext());
}
return sInstance;
}
}

Context的种类

根据Context依托的组件以及用途不同,我们可以将Context分为如下几种。

  1. Application:Android应用中的默认单例类,在Activity或者Service中通过getAppliction()获取到我们的Context实例,通过context.getApplication()可以获取对应全局的唯一的Context实例
  2. Activity/Service:这个两个类都是ContextWrapper的子类,在这两个类中可以通过getBaseContext()获得到它们的Context实例,不同的Activity或者Service实例,它们的Context都是独立的,不能复用
  3. BroadcastReceiver:和Activity以及Service不同,BroadcatReceiver并不是Context子类,而是在回调函数onReceive()中由Android框架转入一个Context实例。系统传入的这个Context的这个Context实例是经过剪裁的,它不能调用registerReciver()以及bindSerivce()这两个函数
  4. ContentProvider: 同样的,ContextProvider也不是Context的子类,但是在创建是系统回传一个Context实例,这样在ContextProvider中可以通过getContext()函数获取。如果ContextProvider和调用者处于相同的应用进程中,那么getContext()将返回用工全局唯一的Context实例,如果是其他进程调用的ContextProvider,那么ContextProvider将持有自身所在的进程Context实例。

    不同的Context的对比

不同组件中的Context能提供的功能不尽相同,总结如下

功能 Application Activity Service BroadcastReciver ContentProivder
显示Dialog NO YES NO NO NO
启动Activity NO[1] YES NO[1] NO[1] NO[1]
实现Layout Inflation NO[2] YES NO[2] NO[2] NO[2]
启动Service YES YES YES YES YES
绑定Service YES YES YES YES NO
发送Broadcast YES YES YES YES YES
注册 YES YES YES YES NO[3]
加载资源Resource YES YES YES YES YES

NO[1]:对应的组件并不是真的不可以启动Activity,而是建议不要这么做,因为这些组件会在新的Task中创建Activity,而不是在原来的Task中。

NO[2]:也表示不建议这么做,在非Activity中进行LayoutInflation,会使用系统默认的主题,而不是应用中设置的主题

NO[3]:在Android 4.2和以上的系统上,如果注册的BroadcastRecvicer是NULL是可以的,用来获取skicky广播的当前值

获得Context方式

  1. 在Activity中使用getContext()
  2. 在Fragment使用在onAttch(Context context)方法中将Context设置为全局
  3. 在View和ViewGroup也有getContext(),获得Conext是View或者ViewGroup所在的Activity

debug